package 编程模式;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 单例模式实现
 * 单例模式指在程序中，此类的实例只能且只能有一个；单例模式主要用于多线程下，资源共享情况下，保证操作安全而设计的；
 */
public class SingleMode {
    /**
     * 为什么用volatile关键字？
     * 答：程序在运行期间，jvm会进行步骤调优，例如可以将private volatile static SingleMode uniqueInstance = new SingleMode();分为
     * 三个步骤：
     *  1.在堆内存上分配对象的内存空间
     *  2.在堆内存上初始化对象
     *  3.设置instance指向刚分配的内存地址
     * 但是由于 JVM 具有指令重排的特性，执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题，但是在多线程环境下会导致一个线程获得还没
     * 有初始化的实例。例如，线程 T1 执行了 1 和 3，此时 T2 调用 `getUniqueInstance`() 后发现 `uniqueInstance` 不为空，因此返回
     * `uniqueInstance`，但此时 `uniqueInstance` 还未被初始化。
     * volatile作用就是屏蔽cpu cache，直接让cpu从内存中读取数据，屏蔽jvm对指令优化重拍；
     * volatile与synchronized的区别：
     *      1. 从体量上说：synchronized是重量级的，volatile是轻量级的；
     *      2. 从线程安全性上来说，synchronized保证了线程原子性，volatile只保证线程的可见性；
     *      3. 从修饰类型上说：synchronized修饰的是代码块，包括对象，方法；volatile只修饰变量；
     */
    private volatile static SingleMode uniqueInstance;

    private SingleMode() {
    }

    public SingleMode getUniqueInstance() {
        if (uniqueInstance == null) {
            Lock lock = new ReentrantLock();
            lock.lock();
            if (uniqueInstance == null) uniqueInstance = new SingleMode();
            lock.unlock();
        }
        return uniqueInstance;
    }

    public SingleMode getUniqueInstance2() {
        if (uniqueInstance == null) {
            synchronized (SingleMode.class){ // SingleMode.class也是一个对象，代表的是Class对象，所有的对象创建都要从Class中提取他自己的数据结构
                if (uniqueInstance == null) uniqueInstance = new SingleMode();
            }
        }
        return uniqueInstance;
    }
}
